Résumé : Ce guide présente une approche pratique pour conteneuriser une application full stack (frontend + backend + base de données), la tester en local avec Docker Compose puis la déployer en production sur Kubernetes. Il inclut des exemples de Dockerfile, de manifests Kubernetes, des commandes essentielles et des bonnes pratiques CI/CD et sécurité.
1. Architecture cible
Exemple d'architecture pour une app full stack :
- Frontend : React (build static + serveur Nginx)
- Backend : API Node.js/Express (ou Flask/Django)
- Base de données : PostgreSQL (stateful)
- Ingress : Ingress Controller (NGINX, Traefik)
- Observabilité : Prometheus + Grafana + EFK (Elasticsearch, Fluentd, Kibana)
2. Docker : conteneurisation
Dockerfile exemple — Backend Node.js
# Dockerfile (backend)
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Dockerfile exemple — Frontend (React) servie par Nginx
# Dockerfile (frontend)
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:stable-alpine
COPY --from=build /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx","-g","daemon off;"]
Docker Compose (local)
# docker-compose.yml (développement local)
version: "3.8"
services:
db:
image: postgres:15
environment:
POSTGRES_USER: appuser
POSTGRES_PASSWORD: changeme
POSTGRES_DB: appdb
volumes:
- db-data:/var/lib/postgresql/data
networks:
- appnet
backend:
build: ./backend
ports:
- "3000:3000"
environment:
DATABASE_URL: postgres://appuser:changeme@db:5432/appdb
depends_on:
- db
networks:
- appnet
frontend:
build: ./frontend
ports:
- "8080:80"
depends_on:
- backend
networks:
- appnet
volumes:
db-data:
networks:
appnet:
Avec Docker Compose, tu peux simuler l’environnement complet en local avant d'aller sur Kubernetes.
3. Kubernetes : manifests essentiels
Deployment + Service — Backend
# backend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
spec:
replicas: 3
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: backend
image: myrepo/backend:latest
ports:
- containerPort: 3000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-secret
key: DATABASE_URL
---
# backend-service.yaml
apiVersion: v1
kind: Service
metadata:
name: backend
spec:
selector:
app: backend
ports:
- protocol: TCP
port: 3000
targetPort: 3000
type: ClusterIP
StatefulSet — PostgreSQL
# postgres-statefulset.yaml (simplifié)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pg-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: "postgres"
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15
ports:
- containerPort: 5432
env:
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: db-secret
key: POSTGRES_USER
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: POSTGRES_PASSWORD
volumeMounts:
- name: pgdata
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: pgdata
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
Ingress (exemple NGINX)
# ingress.yaml (exemple)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: backend
port:
number: 3000
4. CI/CD et workflow
Pipeline typique :
- Tests unitaires & linting
- Build d'images Docker (frontend & backend)
- Scan de sécurité d'images (ex. Trivy)
- Push vers un registry (Docker Hub, GitHub Container Registry, GCR, ECR)
- Déploiement sur K8s (kubectl apply / Helm / Argo CD)
Extrait d'étape GitHub Actions pour builder et push :
# .github/workflows/ci.yml (extrait)
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build backend image
run: docker build -t ghcr.io/${{ github.repository_owner }}/backend:${{ github.sha }} ./backend
- name: Push backend image
run: |
echo ${{ secrets.GHCR_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker push ghcr.io/${{ github.repository_owner }}/backend:${{ github.sha }}
5. Observabilité et monitoring
Outils recommandés :
- Logs : EFK (Elasticsearch, Fluentd/Fluent Bit, Kibana)
- Métriques : Prometheus + Grafana
- Tracing distribué : Jaeger / OpenTelemetry
Astuce : exporter métriques app (Prometheus client) et ajouter probes readiness/liveness dans les manifests K8s.
6. Sécurité et bonnes pratiques
- Utiliser Secrets (Kubernetes Secrets / External Secrets) — ne commit jamais les credentials.
- Limiter les permissions via RBAC et limiter les capacités des containers (securityContext).
- Scanner les images pour vulnérabilités (Trivy / Clair).
- Activer TLS au niveau de l'Ingress (Let's Encrypt + cert-manager).
- Garder les images légères (alpine) et appliquer les mises à jour régulières.
7. Commandes utiles
# build et push
docker build -t myrepo/backend:1.0 ./backend
docker push myrepo/backend:1.0
# déployer sur k8s
kubectl apply -f k8s/backend-deployment.yaml
kubectl rollout status deployment/backend
# logs & debug
kubectl logs -l app=backend -c backend
kubectl get pods -o wide
# accéder à un pod
kubectl exec -it -- /bin/sh
8. Exemples de problèmes courants & solutions
Pods CrashLoopBackOff
Vérifier les logs (`kubectl logs`), les variables d'environnement (DB url), et readiness/liveness probes.
Problèmes de persistance
Valider les PersistentVolumeClaims et la classe de stockage (StorageClass) utilisée sur le cluster.
Ingress non accessible
Vérifier que l'Ingress Controller est installé et que les annotations/host correspondent aux DNS et au certificat TLS.
9. Checklist de déploiement
- Conteneuriser & tester localement (Docker Compose)
- Configurer registry privé/public sécurisé
- Déployer manifests K8s en staging
- Mettre en place monitoring et alerting
- Exécuter tests de charge et résilience
- Automatiser via GitOps (Argo CD) ou pipeline CI/CD